package com.pactera.asmp.server.common.bug.impl;

import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.fasterxml.jackson.databind.JsonNode;
import com.pactera.asmp.server.entity.TestReportJiraDTO;
import com.pactera.asmp.server.entity.TestReportJiraEntity;
import com.pactera.asmp.server.utils.HttpService;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.entity.StringEntity;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.util.EntityUtils;
import org.assertj.core.util.Maps;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.pactera.asmp.server.common.logprint.utils.CollectionUtils;
import com.pactera.asmp.server.entity.ProjectProductionConfig;
import com.pactera.asmp.server.pojo.TestReport;
import com.pactera.asmp.server.utils.HttpResult;
import org.springframework.web.client.RestTemplate;

import javax.xml.ws.soap.Addressing;

@Service
public class JiraBugHandlerImpl extends AbstractBugHandler {

    private static final Logger logger = LoggerFactory.getLogger(JiraBugHandlerImpl.class);

    private static final String GET_REST_PROJECT_URL = "rest/api/2/project";

    private static final String POST_REST_ISSUE_URL = "rest/api/2/issue";

    private static final String GET_REST_PRIORITY_URL = "rest/api/2/priority";

    private static final String GET_REST_ISSUETYPE_URL = "rest/api/2/issuetype";

    private static final String POST_REST_ISSUE_ATTACHMENT_URL = "rest/api/2/issue/%s/attachments";

    private static final String BUG_URL = "browse/%s";

    @Value("${minio.url}")
    private String fileUrl;

    @Value("${minio.bucketName}")
    private String buckName;
    @Autowired
    private HttpService httpService;
    @Autowired
    private RestTemplate restTemplate;

    /**
     * 提交bug到jira平台
     * 
     * @param title
     * @param stepContent
     * @param testReport
     * @param projectProductionConfig
     * @return
     */
    @Override
    public String[] handleBug(String title, String stepContent, TestReport testReport,
            ProjectProductionConfig projectProductionConfig) {
        // The payload definition using the Jackson library
        JsonNodeFactory jnf = JsonNodeFactory.instance;
        ObjectNode payload = jnf.objectNode();
        {
            ObjectNode fields = payload.putObject("fields");
            {
                ObjectNode assignee = fields.putObject("assignee");
                {
                    assignee.put("name", projectProductionConfig.getBugAccount());
                }
                fields.put("description", "[步骤]\n\r" + stepContent.replaceAll("<br/>", "\n\r") +"\n\r[结果]\n\r " + testReport.getContent() + "\n\r[期望]\n\r正常执行");
                ObjectNode issuetype = fields.putObject("issuetype");
                {
                    issuetype.put("id", this.getBugId(projectProductionConfig));
                }
                if (StringUtils.isNotEmpty(projectProductionConfig.getBugLabels())) {
                    String[] labelStrs = projectProductionConfig.getBugLabels().split(",");
                    ArrayNode labels = fields.putArray("labels");
                    for (String label : labelStrs) {
                        labels.add(label);
                    }
                }
                ObjectNode priority = fields.putObject("priority");
                {
                    priority.put("id", projectProductionConfig.getPriority());
                }
                ObjectNode project = fields.putObject("project");
                {
                    project.put("id", projectProductionConfig.getBugProjectId());
                }
                ObjectNode reporter = fields.putObject("reporter");
                {
                    reporter.put("name", projectProductionConfig.getBugAccount());
                }
                fields.put("summary", title);
            }
        }

        String issueUrl;
        if (projectProductionConfig.getBugUrl().endsWith("/")) {
            issueUrl = projectProductionConfig.getBugUrl() + POST_REST_ISSUE_URL;
        } else {
            issueUrl = projectProductionConfig.getBugUrl() + "/" + POST_REST_ISSUE_URL;
        }
        try {
            String token = Base64.getEncoder().encodeToString(StringUtils.join(projectProductionConfig.getBugAccount(), ":", projectProductionConfig.getBugPassword())
                    .getBytes(StandardCharsets.UTF_8));
            HttpResult httpResult = httpService.doPost(issueUrl, new StringEntity(payload.toString(), "utf8"), new HashMap<String, String>() {{
                put("Authorization", "Basic " + token);
                put("Content-Type", "application/json;charset=UTF-8");
            }});

            if (null == httpResult || httpResult.getCode().intValue() != 201) {
                logger.error("handleBug error.HttpStatusCode:{}", httpResult.getCode());
                return null;
            }
            Map<String, Object> issueMap = JSONObject.parseObject(httpResult.getBody(), new TypeReference<Map<String, Object>>() {});
            if (MapUtils.isNotEmpty(issueMap)) {
                String key = String.valueOf(issueMap.get("key"));
                String keyUrl;
                if (projectProductionConfig.getBugUrl().endsWith("/")) {
                    keyUrl = projectProductionConfig.getBugUrl() + String.format(BUG_URL, key);
                } else {
                    keyUrl = projectProductionConfig.getBugUrl() + "/" + String.format(BUG_URL, key);
                }
                // 上传附件
                if (StringUtils.isNotEmpty(testReport.getLogFileIndex())
                        || StringUtils.isNotEmpty(testReport.getVideoFileIndex())) {
                    this.addAttachment(testReport, projectProductionConfig, fileUrl, buckName, key);
                }
                return new String[] { key, keyUrl };
            }
        } catch (Exception ex) {
            logger.error("handleBug error. {}, {}", ex.getMessage());
        }
        return null;
    }
    @Override
    public String[] handleBugForMonkey(String title, String stepContent, TestReportJiraEntity testReportJiraEntity, TestReportJiraDTO testReportJiraDTO,
                                       ProjectProductionConfig projectProductionConfig) {
        // The payload definition using the Jackson library
        JsonNodeFactory jnf = JsonNodeFactory.instance;
        ObjectNode payload = jnf.objectNode();
        {
            ObjectNode fields = payload.putObject("fields");
            {
                ObjectNode assignee = fields.putObject("assignee");
                {
                    assignee.put("name", projectProductionConfig.getBugAccount());
                }
                fields.put("description", stepContent);
                ObjectNode issuetype = fields.putObject("issuetype");
                {
                    issuetype.put("id", "11002");
                }
                if (StringUtils.isNotEmpty(projectProductionConfig.getBugLabels())) {
                    String[] labelStrs = projectProductionConfig.getBugLabels().split(",");
                    ArrayNode labels = fields.putArray("labels");
                    for (String label : labelStrs) {
                        labels.add(label);
                    }
                }
                ObjectNode priority = fields.putObject("priority");
                {
                    priority.put("id", testReportJiraDTO.getPriority());
                }
                ArrayNode components = fields.putArray("components");
                {
                    ObjectNode component = jnf.objectNode();
                    component.put("id", "14376");
                    components.add(component);
                }
                ObjectNode customfield_11002 = fields.putObject("customfield_11002");
                {
                    customfield_11002.put("id", testReportJiraDTO.getCustomfield_11002());
                }
                ObjectNode customfield_12605 = fields.putObject("customfield_12605");
                {
                    customfield_12605.put("id", testReportJiraDTO.getCustomfield_12605());
                }
                ObjectNode customfield_12501 = fields.putObject("customfield_12501");
                {
                    customfield_12501.put("id", testReportJiraDTO.getCustomfield_12501());
                }
                ObjectNode project = fields.putObject("project");
                {
                    project.put("id", testReportJiraDTO.getProject());
                }
                fields.put("customfield_12911", testReportJiraDTO.getCustomfield_12911());
                fields.put("customfield_11003", testReportJiraDTO.getCustomfield_11003());
                fields.put("customfield_11004", testReportJiraDTO.getCustomfield_11004());
//                fields.put("customfield_11004", "14030");
                fields.put("summary", title);
            }
        }

        String issueUrl;
        if (projectProductionConfig.getBugUrl().endsWith("/")) {
            issueUrl = projectProductionConfig.getBugUrl() + POST_REST_ISSUE_URL;
        } else {
            issueUrl = projectProductionConfig.getBugUrl() + "/" + POST_REST_ISSUE_URL;
        }
        try {
            String token = Base64.getEncoder().encodeToString(String.format("%s:%s",projectProductionConfig.getBugAccount(), projectProductionConfig.getBugPassword())
                    .getBytes(StandardCharsets.UTF_8));
            HttpResult httpResult = new HttpResult();
                    logger.info("创建 jira url {}, token 为 {}, payload {}" ,issueUrl, token, payload.toString());
//            HttpResult httpResult = httpService.doPost(issueUrl, new StringEntity(payload.toString(), "utf8"), new HashMap<String, String>() {{
//                put("Authorization", "Basic " + token);
//                put("Content-Type", "application/json;charset=UTF-8");
//            }});

            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(MediaType.APPLICATION_JSON);
            headers.set("Authorization", "Basic " + token); // 认证方式：Bearer Token（常见）
            headers.set("Content-Type", "application/json;charset=UTF-8"); // 认证方式：Bearer Token（常见）
            // 若用Basic Auth：headers.setBasicAuth("username", "password");

            // 5. 封装请求实体（请求头+请求体）
            HttpEntity<ObjectNode> requestEntity = new HttpEntity<>(payload, headers);
            ResponseEntity<String> response = restTemplate.postForEntity(issueUrl, requestEntity, String.class);
            httpResult.setCode(response.getStatusCodeValue());
            if (response.getBody() != null) {
                httpResult.setBody(response.getBody());
            }
            if (null == httpResult || httpResult.getCode().intValue() != 201) {
                logger.error("handleBug error.HttpStatusCode:{}", httpResult.getCode());
                return null;
            }
            Map<String, Object> issueMap = JSONObject.parseObject(httpResult.getBody(), new TypeReference<Map<String, Object>>() {});
            if (MapUtils.isNotEmpty(issueMap)) {
                String key = String.valueOf(issueMap.get("key"));
                String keyUrl;
                if (projectProductionConfig.getBugUrl().endsWith("/")) {
                    keyUrl = projectProductionConfig.getBugUrl() + String.format(BUG_URL, key);
                } else {
                    keyUrl = projectProductionConfig.getBugUrl() + "/" + String.format(BUG_URL, key);
                }
                // 上传附件
                if (StringUtils.isNotEmpty(testReportJiraEntity.getFileId())) {
                    this.addAttachmentForMonkey(testReportJiraEntity, projectProductionConfig, fileUrl, buckName, key);
                }
                return new String[] { key, keyUrl };
            }
        } catch (Exception ex) {
            logger.error("handleBug error. {}, {}", ex.getMessage());
        }
        return null;
    }

    /**
     * 获取jira上的项目列表
     * 
     * @param projectProductionConfig
     * 
     * @return
     */
    @Override
    public Map<String, String> getProjectList(ProjectProductionConfig projectProductionConfig) {
        String url;
        if (projectProductionConfig.getBugUrl().endsWith("/")) {
            url = projectProductionConfig.getBugUrl() + GET_REST_PROJECT_URL;
        } else {
            url = projectProductionConfig.getBugUrl() + "/" + GET_REST_PROJECT_URL;
        }
        try {
            String token = Base64.getEncoder().encodeToString(StringUtils
                    .join(projectProductionConfig.getBugAccount(), ":", projectProductionConfig.getBugPassword())
                    .getBytes(StandardCharsets.UTF_8));
            HttpResult httpResult= httpService.doGet(url, Maps.newHashMap("Authorization", "Basic " + token));
            if (null == httpResult || httpResult.getCode().intValue() != 200) {
                logger.error("getProjectList error.HttpStatusCode:{}", httpResult.getCode());
                return new HashMap<>();
            }

            List<Map<String, Object>> projects = JSONObject.parseObject(httpResult.getBody(), new TypeReference<List<Map<String, Object>>>() {});
            if (CollectionUtils.isNotEmpty(projects)) {
                Map<String, String> resultMap = new HashMap<>();
                for (Map<String, Object> project : projects) {
                    resultMap.put(String.valueOf(project.get("id")), String.valueOf(project.get("name")));
                }
                return resultMap;
            }
        } catch (Exception ex) {
            logger.error("getProjectList error. {}, {}", ex.getMessage());
        }
        return new HashMap<>();
    }

    /**
     * 上传附件
     * 
     * @param testReport
     * @param projectProductionConfig
     * @param fileUrl
     * @param buckName
     * @param key
     */
    private void addAttachment(TestReport testReport, ProjectProductionConfig projectProductionConfig,
            String fileUrl, String buckName, String key) {

        File logFile = null;
        File videoFile = null;
        File canFile = null;
        File reportFile = null;
        String attachmentUrl;
        try {
            // 发起请求,服务地址，请求参数，返回消息体的数据类型
            if (projectProductionConfig.getBugUrl().endsWith("/")) {
                attachmentUrl = projectProductionConfig.getBugUrl() + String.format(POST_REST_ISSUE_ATTACHMENT_URL, key);
            } else {
                attachmentUrl = projectProductionConfig.getBugUrl() + "/" + String.format(POST_REST_ISSUE_ATTACHMENT_URL, key);
            }

            MultipartEntityBuilder builder = MultipartEntityBuilder.create();
            builder.setCharset(Charset.forName("utf-8"));

            if (StringUtils.isNotEmpty(testReport.getLogFileIndex())) {
                String logPath = fileUrl + "/" + buckName + "/" + testReport.getLogFileIndex();
                try {
                    URL logUrl = new URL(logPath);
                    logFile = File.createTempFile(testReport.getLogFileIndex(), ".txt");

                    InputStream inputStream = logUrl.openStream();
                    OutputStream outputStream = new FileOutputStream(logFile);
                    byte[] buffer = new byte[1024];
                    int length;
                    while ((length = inputStream.read(buffer)) != -1) {
                        outputStream.write(buffer, 0, length);
                    }
                    inputStream.close();
                    outputStream.close();
                    builder.addBinaryBody("file", logFile);
                } catch (Exception e) {
                    logger.error(e.getMessage());
                }
            }
            if (StringUtils.isNotEmpty(testReport.getVideoFileIndex())) {
                String videoPath = fileUrl + "/" + buckName + "/" + testReport.getVideoFileIndex();
                try {
                    URL videoUrl = new URL(videoPath);
                    videoFile = File.createTempFile(
                            testReport.getVideoFileIndex().substring(0, testReport.getVideoFileIndex().lastIndexOf(".")),
                            videoPath.substring(videoPath.lastIndexOf(".")));

                    InputStream inputStream = videoUrl.openStream();
                    OutputStream outputStream = new FileOutputStream(videoFile);
                    byte[] buffer = new byte[1024];
                    int length;
                    while ((length = inputStream.read(buffer)) != -1) {
                        outputStream.write(buffer, 0, length);
                    }
                    inputStream.close();
                    outputStream.close();
                    builder.addBinaryBody("file", videoFile);
                } catch (Exception e) {
                    logger.error(e.getMessage());
                }
            }
            if (StringUtils.isNotEmpty(testReport.getCanLogFileId())) {
                String canPath = fileUrl + "/" + buckName + "/" + testReport.getCanLogFileId();
                try {
                    URL canUrl = new URL(canPath);
                    canFile = File.createTempFile(
                            testReport.getCanLogFileId().substring(0, testReport.getCanLogFileId().lastIndexOf(".")),
                            canPath.substring(canPath.lastIndexOf(".")));

                    InputStream inputStream = canUrl.openStream();
                    OutputStream outputStream = new FileOutputStream(canFile);
                    byte[] buffer = new byte[1024];
                    int length;
                    while ((length = inputStream.read(buffer)) != -1) {
                        outputStream.write(buffer, 0, length);
                    }
                    inputStream.close();
                    outputStream.close();
                    builder.addBinaryBody("file", canFile);
                } catch (Exception e) {
                    logger.error(e.getMessage());
                }
            }
            if (StringUtils.isNotEmpty(testReport.getHtmlReportFileId())) {
                String reportPath = fileUrl + "/" + buckName + "/" + testReport.getHtmlReportFileId();
                try {
                    URL reportUrl = new URL(reportPath);
                    reportFile = File.createTempFile(
                            testReport.getHtmlReportFileId().substring(0, testReport.getHtmlReportFileId().lastIndexOf(".")),
                            reportPath.substring(reportPath.lastIndexOf(".")));

                    InputStream inputStream = reportUrl.openStream();
                    OutputStream outputStream = new FileOutputStream(reportFile);
                    byte[] buffer = new byte[1024];
                    int length;
                    while ((length = inputStream.read(buffer)) != -1) {
                        outputStream.write(buffer, 0, length);
                    }
                    inputStream.close();
                    outputStream.close();
                    builder.addBinaryBody("file", reportFile);
                } catch (Exception e) {
                    logger.error(e.getMessage());
                }
            }

            org.apache.http.HttpEntity multipart = builder.build();

            String token = Base64.getEncoder().encodeToString(StringUtils.join(projectProductionConfig.getBugAccount(), ":", projectProductionConfig.getBugPassword())
                    .getBytes(StandardCharsets.UTF_8));
            HttpResult httpResult = httpService.doPost(attachmentUrl, multipart, new HashMap<String, String>() {{
                put("Authorization", "Basic " + token);
                put("Accept", "application/json");
                put("X-Atlassian-Token", "no-check");
            }});

            if (null == httpResult || httpResult.getCode().intValue() != 200) {
                logger.error("addAttachment error.HttpStatusCode:{}", httpResult.getCode());
            }
        } catch (Exception ex) {
            logger.error("addAttachment error. {}, {}", ex.getMessage());
        } finally {
            if (null != logFile) {
                logFile.delete();
            }
            if (null != videoFile) {
                videoFile.delete();
            }
        }
    }

    private void addAttachmentForMonkey(TestReportJiraEntity testReportJiraEntity, ProjectProductionConfig projectProductionConfig,
            String fileUrl, String buckName, String key) {

        File logFile = null;
        String attachmentUrl;
        try {
            // 发起请求,服务地址，请求参数，返回消息体的数据类型
            if (projectProductionConfig.getBugUrl().endsWith("/")) {
                attachmentUrl = projectProductionConfig.getBugUrl() + String.format(POST_REST_ISSUE_ATTACHMENT_URL, key);
            } else {
                attachmentUrl = projectProductionConfig.getBugUrl() + "/" + String.format(POST_REST_ISSUE_ATTACHMENT_URL, key);
            }

            MultipartEntityBuilder builder = MultipartEntityBuilder.create();
            builder.setCharset(Charset.forName("utf-8"));

            String fileId = testReportJiraEntity.getFileId();
            if (StringUtils.isNotEmpty(fileId)) {
                String logPath = fileUrl + "/" + buckName + "/" + fileId;
                try {
                    URL logUrl = new URL(logPath);
                    logFile = File.createTempFile(fileId, ".txt");

                    InputStream inputStream = logUrl.openStream();
                    OutputStream outputStream = new FileOutputStream(logFile);
                    byte[] buffer = new byte[1024];
                    int length;
                    while ((length = inputStream.read(buffer)) != -1) {
                        outputStream.write(buffer, 0, length);
                    }
                    inputStream.close();
                    outputStream.close();
                    builder.addBinaryBody("file", logFile);
                } catch (Exception e) {
                    logger.error(e.getMessage());
                }
            }

            org.apache.http.HttpEntity multipart = builder.build();

            String token = Base64.getEncoder().encodeToString(String.format("%s:%s",projectProductionConfig.getBugAccount(), projectProductionConfig.getBugPassword())
                    .getBytes(StandardCharsets.UTF_8));
            logger.info("添加 jira attachmentUrl {}, token 为 {}, multipart {}" ,attachmentUrl, token, multipart.getContentType().getName());
            HttpResult httpResult = httpService.doPost(attachmentUrl, multipart, new HashMap<String, String>() {{
                put("Authorization", "Basic " + token);
                put("Accept", "application/json");
                put("X-Atlassian-Token", "no-check");
            }});

            if (null == httpResult || httpResult.getCode().intValue() != 200) {
                logger.error("addAttachment error.HttpStatusCode:{}", httpResult.getCode());
            }
        } catch (Exception ex) {
            logger.error("addAttachment error. {}, {}", ex.getMessage());
        } finally {
            if (null != logFile) {
                logFile.delete();
            }
        }
    }

    @Override
    public Map<String, String> getProductionList(ProjectProductionConfig projectProductionConfig) {
        // donoting
        return null;
    }

    /**
     * 获取jira上的等级列表
     * 
     * @param projectProductionConfig
     * 
     * @return
     */
    @Override
    public Map<String, String> getPriorityList(ProjectProductionConfig projectProductionConfig) {
        String url;
        if (projectProductionConfig.getBugUrl().endsWith("/")) {
            url = projectProductionConfig.getBugUrl() + GET_REST_PRIORITY_URL;
        } else {
            url = projectProductionConfig.getBugUrl() + "/" + GET_REST_PRIORITY_URL;
        }
        try {
            String token = Base64.getEncoder().encodeToString(StringUtils
                    .join(projectProductionConfig.getBugAccount(), ":", projectProductionConfig.getBugPassword())
                    .getBytes(StandardCharsets.UTF_8));
            HttpResult httpResult= httpService.doGet(url, Maps.newHashMap("Authorization", "Basic " + token));
            if(null == httpResult || httpResult.getCode().intValue() != 200) {
                logger.error("getPriorityList error.HttpStatusCode:{}", httpResult.getCode());
                return new HashMap<>();
            }
            List<Map<String, Object>> prioritys = JSONObject.parseObject(httpResult.getBody(), new TypeReference<List<Map<String, Object>>>() {});
            if (CollectionUtils.isNotEmpty(prioritys)) {
                Map<String, String> resultMap = new HashMap<>();
                for (Map<String, Object> priority : prioritys) {
                    resultMap.put(String.valueOf(priority.get("id")), String.valueOf(priority.get("name")));
                }
                return resultMap;
            }
        } catch (Exception ex) {
            logger.error("getPriorityList error. {}, {}", ex.getMessage());
        }
        return new HashMap<>();
    }

    /**
     * 获取bugId
     * 
     * @param projectProductionConfig
     * 
     * @return
     */
    private String getBugId(ProjectProductionConfig projectProductionConfig) {
        String url;
        if (projectProductionConfig.getBugUrl().endsWith("/")) {
            url = projectProductionConfig.getBugUrl() + GET_REST_ISSUETYPE_URL;
        } else {
            url = projectProductionConfig.getBugUrl() + "/" + GET_REST_ISSUETYPE_URL;
        }
        try {

            String token = Base64.getEncoder().encodeToString(StringUtils
                    .join(projectProductionConfig.getBugAccount(), ":", projectProductionConfig.getBugPassword())
                    .getBytes(StandardCharsets.UTF_8));
            logger.info("getBugId jira url {}, token 为 {}, payload {}" ,url, token);
            HttpResult httpResult= httpService.doGet(url, Maps.newHashMap("Authorization", "Basic " + token));
            if(null == httpResult || httpResult.getCode().intValue() != 200) {
                logger.error("getBugId error.HttpStatusCode:{}", httpResult.getCode());
                logger.error("use default value:10004");
                return "10004"; // 故障default值
            }
            List<Map<String, Object>> types = JSONObject.parseObject(httpResult.getBody(), new TypeReference<List<Map<String, Object>>>() {});
            if (CollectionUtils.isNotEmpty(types)) {
                for (Map<String, Object> type : types) {
                    String name = String.valueOf(type.get("name"));
                    if(StringUtils.equalsIgnoreCase(name, "Bug") || StringUtils.equals(name, "故障")) {
                        return String.valueOf(type.get("id"));
                    }
                }
            }
        } catch (Exception ex) {
            logger.error("getBugId error. {}, {}", ex.getMessage());
            logger.error("use default value:10004");
        }
        return "10004"; // 故障default值
    }
}
